;;;;;;;;;;;;;;;;;
;;;;; Notes ;;;;;
;;;;;;;;;;;;;;;;;

;; luaexec("Add-Ons/8608/assembler-8608.lua");
;; AssembleBuildFile("Add-Ons/8608/programs/auios.asm", "32 32 8");

;; `ads -1` pops into void.
;; `lda *s-3` Get first argument.
;; Use Q as the cursor (think qursor).
;; Use P as the data ptr.

;; Function template:
;; ```
;; Processes data in the keyboard queue
;; No args
;; Returns nothing
; FUNC handleInput: {
; }
;; ```

;;;;;;;;;;;;;;;;;;;;;;
;;;;; Code Style ;;;;;
;;;;;;;;;;;;;;;;;;;;;;

;; DEFINES
;; functionName
;; variable_name
;; label_name:
;; .sub_label_name:

;;;;;;;;;;;;;;;;;;;;
;;;;; Includes ;;;;;
;;;;;;;;;;;;;;;;;;;;

.include macros.asm

.include defs\hwdefs.asm

.include defs\clrdefs.asm

.include defs\keydefs.asm

;; Defines
.define TRUE $01
.define FALSE $00
.define CURSOR_BLINK_TICKS 20
.define CONST_ROM $0200

;; Constants
.org CONST_ROM
str_startup: "Program running\0"

;; Variables
.org SYSRAM
int_tmp_p: word
int_tmp_s: word
int_stack: byte[(128-4)]
stack: byte[128]

.org USERRAM
cursor_timer: byte
cursor_pos: word
shift_down: byte

;;;;;;;;;;;;;;;;
;;;;; Boot ;;;;;
;;;;;;;;;;;;;;;;

.org SYSROM
jmp main

;; Main program entry.
;; In: None
;; Out:: None
FUNC main: {
    ; Set stack and interrupt locations
    lds stack
    ldv interrupt

    ;; Clear screen
    jss cls

    ;; Print startup
    ldp str_startup
    jss print

    ;; Run main
    jmp mainLoop
}

;; Main program loop.
;; In: None
;; Out:: None
FUNC mainLoop: {
    idi
    lda 1 ;; Sleep for 1 tick * 32ms
    sta *io.timer
    hlt
    jss handleInput
    jss blinkCursor
    ien
jpr }

FUNC interrupt: {
    run
    irt
jpr }

;; Resets the cursor back to the start of the screen space
;; In: None
;; Out: None
FUNC resetCursor: {
    ldq screen.char
    stq *cursor_pos
    rts
}

;; Clears the screen and resets the cursor to the top left corner.
;; In: None
;; Out:: None
FUNC cls: {
    jss resetCursor
    ldc $00 ;; Blank
    ldp screen.color
    ldb CLR_BLACK ;; Black
    .cls_loop: {
        stc *q++
        stb *p++
        lda ql
        cmp (lo(COLOR))
        jnz .cls_loop
        lda qh
        cmp (hi(COLOR))
    jnz }
    jss resetCursor
    rts
}

;; Prints a string where the cursor is currently at.
;; In:
;;  q = Cursor position
;;  p = Pointer to string
;; Out:: None
FUNC print: {
    ;; Set cursor color
    psh p
    lda CLR_BLACK
    jss setCursorColor
    pop p

    ;; Print string
    lda *p++
    jpz {
        sta *q++
        stq *cursor_pos
        jmp print
    jpr }

    lda CLR_WHITE
    jss setCursorColor

    rts
}

;; Test.
;; In: None
;; Out: None
FUNC test: {
    ads 1
    .define .var *s-1
    lda 5
    sta .var
    inc .var
    
    lda .var
    jss printHex

    ads -1
    rts
}

;; Prints A as hex code on screen to the cursor position
;; In:
;;  a = value
;; Out: None
FUNC printHex: {
    psh a
    ldq *cursor_pos
    lda CLR_BLACK
    jss setCursorColor
    pop a

    ldp *cursor_pos
    ldb a ;; Save copy of A to B
    and $F0 ;; Get high nibble
    shr 4
    add "0"
    cmp "9"
    jle {
        add 7
    }
    sta *p++
    stp *cursor_pos

    lda b ;; A = B
    and $0F ;; Get low nibble
    add "0"
    cmp "9"
    jle {
        add 7
    }
    sta *p++
    stp *cursor_pos

    ldq *cursor_pos
    lda CLR_WHITE
    jss setCursorColor

    rts
}

;; Prints A as a char to the cursor.
;; In:
;;  q = Cursor position
;;  a = value
;; Out: None
FUNC printChar: {
    psh a
    lda CLR_BLACK
    jss setCursorColor
    pop a

    ;; Print char to cursor
    sta *q++
    stq *cursor_pos

    lda CLR_WHITE
    jss setCursorColor

    rts
}

;; Sets the cursor color.
;; In:
;;  q = Cursor position
;;  a = Color
;; Out:
;;  p = Color position
FUNC setCursorColor: {
    ldc qh
    ldb ql
    adc $04 ;; Offset cursor by 1024 is its color
    ldp cb ;; Set P as the color position
    sta *p

    rts
}

;; Swaps cursor color when cursor_timer hits 0.
;; In: None
;; Out: None
FUNC blinkCursor: {
    lda *cursor_timer

    ;; If cursor_timer == 0 then swap colors
    IF_ZERO {
        ;; Get color addr from screen pos
        ;; P = screen color
        ldp *cursor_pos
        ldc ph
        ldb pl
        adc $04 ;; cursor_pos offset by 1024 is its color
        ldp cb

        ;; Check if shift is down
        lda *shift_down
        cmp TRUE
        jpz .use_xor_36 ;; If shift_down is TRUE, use $36 for XOR
        ldb $03 ;; Default XOR value
        jmp .do_xor

        .use_xor_36:
        ldb $36 ;; XOR value when shift is down

        .do_xor:
        lda *p
        xor b
        sta *p 

        ;; Reset cursor blink ticks countdown
        lda CURSOR_BLINK_TICKS
    }
    dec a
    sta *cursor_timer

    rts
}

;; Moves the cursor one space forward.
;; In: None
;; Out: None
FUNC moveCursor: {
    ldq *cursor_pos
    lda CLR_BLACK
    jss setCursorColor

    ldp *cursor_pos
    inc p
    stp *cursor_pos

    ldq *cursor_pos
    lda CLR_WHITE
    jss setCursorColor

    rts
}

;; Moves the cursor down one line and to the left
;; In: None
;; Out: None
FUNC newLine: {
    ldq *cursor_pos
    lda CLR_BLACK
    jss setCursorColor

    ldq *cursor_pos
    ldc qh / ldb ql
    lda b / and $C0 / ldb a
    adb 64
    icc c
    ldq cb
    
    stq *cursor_pos

    ldq *cursor_pos
    lda CLR_WHITE
    jss setCursorColor

    rts
}

;; Processes data in the keyboard queue.
;; In: None
;; Out: None
FUNC handleInput: {
    lda *keyboard.queue
    IF_ZERO {
        rts
    }

    jss handleKey
jpr }

;; Handles the current key in A
;; In:
;;  a = inkey
;; Out: None
FUNC handleKey: {
    ldb a
    and $7F ;; Normalize key code
    
    ;; Handle shift key pressed/released
    cmp KEY_SHIFT_LEFT
    jpz .handle_shift_logic
    cmp KEY_SHIFT_RIGHT
    jnz .skip_shift_logic

    .handle_shift_logic: {
        lda b
        and KEY_PRESSED
        shr 7
        sta *shift_down

        ;; Reset cursor color to black
        ldq *cursor_pos
        lda CLR_BLACK
        jss setCursorColor

        rts
    }
    .skip_shift_logic:
    
    ;; Return if key was released
    lda b
    and KEY_PRESSED
    IF_TRUE {
        rts
    }

    ;; Set Q to cursor position
    ldq *cursor_pos

    ;; Trim KEY_PRESSED flag
    lda b
    and $7F

    ;; Clear screen
    cmp KEY_F2
    IF_TRUE {
        jss cls

        lda CLR_WHITE
        jss setCursorColor

        rts
    }

    cmp KEY_F3
    IF_TRUE {
        ldp .str_wew
        jss print
        rts

        STR(.str_wew, "Wew lad")
    }

    cmp KEY_F4
    IF_TRUE {
        jss test
        rts
    }

    cmp KEY_UP
    IF_TRUE {
        ldp .str_up
        jss print
        rts

        STR(.str_up, "Up")
    }

    cmp KEY_DOWN
    IF_TRUE {
        ldp .str_down
        jss print
        rts

        STR(.str_down, "Down")
    }

    cmp KEY_LEFT
    IF_TRUE {
        ldp .str_left
        jss print
        rts

        STR(.str_left, "Left")
    }

    cmp KEY_RIGHT
    IF_TRUE {
        ldp .str_right
        jss print
        rts
        
        STR(.str_right, "Right")
    }

    cmp KEY_ENTER
    IF_TRUE {
        jss newLine
        rts
    }
    
    ; jss printChar
    jss printHex
    rts
}
